package com.virtualperson.service.impl;

import com.alibaba.dashscope.audio.tts.*;
import com.alibaba.dashscope.audio.tts.timestamp.Sentence;
import com.alibaba.dashscope.audio.tts.timestamp.Word;
import com.alibaba.dashscope.audio.tts.timestamp.Phoneme;
import com.alibaba.dashscope.common.ResultCallback;
import com.virtualperson.config.TtsConfig;
import com.virtualperson.dto.TtsRequestDTO;
import com.virtualperson.dto.TtsResponseDTO;
import com.virtualperson.service.TtsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@Service
@RequiredArgsConstructor
public class TtsServiceImpl implements TtsService {

    private final TtsConfig ttsConfig;
    private static final int MAX_WAIT_TIME = 30; // 最大等待时间（秒）
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    @Override
    public TtsResponseDTO synthesize(TtsRequestDTO request) {
        try {
            log.info("开始语音合成，请求参数: {}", request);
            SpeechSynthesizer synthesizer = new SpeechSynthesizer();
            
            // 构建参数
            SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                    .model(request.getModel() != null ? request.getModel() : ttsConfig.getTts().getModel())
                    .text(request.getText())
                    .format(request.getFormat() != null ? request.getFormat() : SpeechSynthesisAudioFormat.valueOf(ttsConfig.getTts().getFormat()))
                    .sampleRate(request.getSampleRate() != null ? request.getSampleRate() : ttsConfig.getTts().getSampleRate())
                    .volume(request.getVolume() != null ? request.getVolume() : ttsConfig.getTts().getVolume())
                    .rate(request.getRate() != null ? request.getRate() : ttsConfig.getTts().getRate())
                    .pitch(request.getPitch() != null ? request.getPitch() : ttsConfig.getTts().getPitch())
                    .apiKey(ttsConfig.getApiKey())
                    .build();

            log.info("语音合成参数: model={}, format={}, sampleRate={}, volume={}, rate={}, pitch={}, text={}", 
                param.getModel(), param.getFormat(), param.getSampleRate(), 
                param.getVolume(), param.getRate(), param.getPitch(),
                param.getText());

            if (Boolean.TRUE.equals(request.getStreamMode())) {
                return handleStreamMode(synthesizer, param);
            } else {
                return handleSyncMode(synthesizer, param);
            }
        } catch (Exception e) {
            log.error("语音合成失败", e);
            return TtsResponseDTO.builder()
                    .status("ERROR")
                    .message(e.getMessage())
                    .build();
        }
    }

    private TtsResponseDTO handleStreamMode(SpeechSynthesizer synthesizer, SpeechSynthesisParam param) {
        log.info("使用流式模式进行语音合成");
        CompletableFuture<TtsResponseDTO> future = new CompletableFuture<>();
        List<byte[]> audioChunks = new ArrayList<>();
        List<Map<String, Object>> timestamps = new ArrayList<>();
        AtomicInteger totalSize = new AtomicInteger(0);
        AtomicBoolean hasValidData = new AtomicBoolean(false);
        AtomicInteger emptyChunksCount = new AtomicInteger(0);
        AtomicInteger chunkCount = new AtomicInteger(0);

        // 设置超时
        ScheduledFuture<?> timeoutFuture = scheduler.schedule(() -> {
            if (!future.isDone()) {
                log.warn("语音合成超时（{}秒），当前状态：空数据块数量={}, 总数据大小={}字节, 总数据块数量={}", 
                    MAX_WAIT_TIME, emptyChunksCount.get(), totalSize.get(), chunkCount.get());
                if (!hasValidData.get()) {
                    future.completeExceptionally(new RuntimeException("语音合成超时，未收到有效数据"));
                }
            }
        }, MAX_WAIT_TIME, TimeUnit.SECONDS);

        synthesizer.call(param, new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                int currentChunk = chunkCount.incrementAndGet();
                
                // 打印完整的响应结果
                log.info("收到语音合成响应 #{}: {}", currentChunk, result);
                
                if (result.getAudioFrame() != null) {
                    ByteBuffer currentData = result.getAudioFrame();
                    // 重置缓冲区位置
                    currentData.rewind();
                    
                    log.info("音频数据块 #{} 详情: position={}, limit={}, capacity={}, remaining={}, hasArray={}, isDirect={}, arrayOffset={}", 
                        currentChunk,
                        currentData.position(),
                        currentData.limit(),
                        currentData.capacity(),
                        currentData.remaining(),
                        currentData.hasArray(),
                        currentData.isDirect(),
                        currentData.hasArray() ? currentData.arrayOffset() : -1);

                    if (currentData.hasRemaining()) {
                        byte[] chunk;
                        if (currentData.hasArray()) {
                            // 如果是数组支持的缓冲区，直接获取底层数组
                            byte[] array = currentData.array();
                            int offset = currentData.arrayOffset() + currentData.position();
                            int length = currentData.remaining();
                            chunk = Arrays.copyOfRange(array, offset, offset + length);
                            log.info("从数组支持的缓冲区获取数据: offset={}, length={}, 数据前16字节={}", 
                                offset, length, bytesToHex(Arrays.copyOfRange(chunk, 0, Math.min(16, chunk.length))));
                        } else {
                            // 如果是直接缓冲区，需要复制数据
                            chunk = new byte[currentData.remaining()];
                            currentData.get(chunk);
                            log.info("从直接缓冲区复制数据: length={}, 数据前16字节={}", 
                                chunk.length, bytesToHex(Arrays.copyOfRange(chunk, 0, Math.min(16, chunk.length))));
                        }
                        
                        if (chunk.length > 0) {
                            audioChunks.add(chunk);
                            totalSize.addAndGet(chunk.length);
                            hasValidData.set(true);
                            log.info("收到有效音频数据块 #{}: 大小={} 字节，累计={} 字节", 
                                currentChunk, chunk.length, totalSize.get());
                        } else {
                            emptyChunksCount.incrementAndGet();
                            log.warn("收到空音频数据块 #{}: 数据长度为0", currentChunk);
                        }
                    } else {
                        emptyChunksCount.incrementAndGet();
                        log.warn("收到空音频数据块 #{}: position={}, limit={}, capacity={}, hasArray={}, isDirect={}", 
                            currentChunk,
                            currentData.position(),
                            currentData.limit(),
                            currentData.capacity(),
                            currentData.hasArray(),
                            currentData.isDirect());
                    }
                } else {
                    log.warn("数据块 #{} 的 AudioFrame 为空", currentChunk);
                }

                if (result.getTimestamp() != null) {
                    log.info("收到时间戳数据 #{}: {}", currentChunk, result.getTimestamp());
                    Sentence sentence = result.getTimestamp();
                    Map<String, Object> sentenceInfo = new HashMap<>();
                    sentenceInfo.put("beginTime", sentence.getBeginTime());
                    sentenceInfo.put("endTime", sentence.getEndTime());
                    
                    List<Map<String, Object>> words = new ArrayList<>();
                    if (sentence.getWords() != null) {
                        for (Word word : sentence.getWords()) {
                            Map<String, Object> wordInfo = new HashMap<>();
                            wordInfo.put("text", word.getText());
                            wordInfo.put("beginTime", word.getBeginTime());
                            wordInfo.put("endTime", word.getEndTime());
                            
                            if (word.getPhonemes() != null) {
                                List<Map<String, Object>> phonemes = new ArrayList<>();
                                for (Phoneme phoneme : word.getPhonemes()) {
                                    Map<String, Object> phonemeInfo = new HashMap<>();
                                    phonemeInfo.put("text", phoneme.getText());
                                    phonemeInfo.put("tone", phoneme.getTone());
                                    phonemeInfo.put("beginTime", phoneme.getBeginTime());
                                    phonemeInfo.put("endTime", phoneme.getEndTime());
                                    phonemes.add(phonemeInfo);
                                }
                                wordInfo.put("phonemes", phonemes);
                            }
                            words.add(wordInfo);
                        }
                    }
                    sentenceInfo.put("words", words);
                    timestamps.add(sentenceInfo);
                }
            }

            @Override
            public void onComplete() {
                // 取消超时任务
                timeoutFuture.cancel(false);
                
                log.info("语音合成完成，状态：空数据块数量={}, 总数据大小={}字节, 总数据块数量={}, 是否收到有效数据={}, 任务ID={}", 
                    emptyChunksCount.get(), totalSize.get(), chunkCount.get(), hasValidData.get(),
                    synthesizer.getLastRequestId());
                
                if (!audioChunks.isEmpty() && hasValidData.get()) {
                    // 合并所有音频数据块
                    byte[] completeAudioData = new byte[totalSize.get()];
                    int offset = 0;
                    for (byte[] chunk : audioChunks) {
                        System.arraycopy(chunk, 0, completeAudioData, offset, chunk.length);
                        offset += chunk.length;
                    }
                    log.info("流式模式音频合成完成，总大小: {} 字节，首包延迟: {} ms", 
                        completeAudioData.length, synthesizer.getFirstPackageDelay());

                    future.complete(TtsResponseDTO.builder()
                            .status("SUCCESS")
                            .taskId(synthesizer.getLastRequestId())
                            .audioData(completeAudioData)
                            .format(param.getFormat().name())
                            .sampleRate(param.getSampleRate())
                            .timestamps(timestamps)
                            .firstPackageDelay(synthesizer.getFirstPackageDelay())
                            .build());
                } else {
                    String errorMsg = String.format("语音合成未收到有效数据：空数据块数量=%d, 总数据大小=%d字节, 总数据块数量=%d, 任务ID=%s", 
                        emptyChunksCount.get(), totalSize.get(), chunkCount.get(), synthesizer.getLastRequestId());
                    log.error(errorMsg);
                    future.completeExceptionally(new RuntimeException(errorMsg));
                }
            }

            @Override
            public void onError(Exception e) {
                // 取消超时任务
                timeoutFuture.cancel(false);
                log.error("流式模式语音合成失败: {}, 任务ID: {}", e.getMessage(), synthesizer.getLastRequestId(), e);
                future.completeExceptionally(e);
            }
        });

        try {
            return future.get();
        } catch (Exception e) {
            log.error("流式语音合成失败: {}, 任务ID: {}", e.getMessage(), synthesizer.getLastRequestId(), e);
            return TtsResponseDTO.builder()
                    .status("ERROR")
                    .message(e.getMessage())
                    .build();
        }
    }

    private TtsResponseDTO handleSyncMode(SpeechSynthesizer synthesizer, SpeechSynthesisParam param) {
        log.info("使用同步模式进行语音合成");
        ByteBuffer audioBuffer = synthesizer.call(param);
        log.info("同步模式调用结果: {}", audioBuffer);
        
        if (audioBuffer == null) {
            log.error("同步模式未获取到音频数据");
            return TtsResponseDTO.builder()
                    .status("ERROR")
                    .message("未获取到音频数据")
                    .build();
        }

        // 将ByteBuffer转换为byte数组
        byte[] audioData;
        if (audioBuffer.hasArray()) {
            // 如果是数组支持的缓冲区，直接获取底层数组
            byte[] array = audioBuffer.array();
            int offset = audioBuffer.arrayOffset() + audioBuffer.position();
            int length = audioBuffer.remaining();
            audioData = Arrays.copyOfRange(array, offset, offset + length);
            log.info("从数组支持的缓冲区获取数据: offset={}, length={}, 数据前16字节={}", 
                offset, length, bytesToHex(Arrays.copyOfRange(audioData, 0, Math.min(16, audioData.length))));
        } else {
            // 如果是直接缓冲区，需要复制数据
            audioData = new byte[audioBuffer.remaining()];
            audioBuffer.get(audioData);
            log.info("从直接缓冲区复制数据: length={}, 数据前16字节={}", 
                audioData.length, bytesToHex(Arrays.copyOfRange(audioData, 0, Math.min(16, audioData.length))));
        }

        log.info("同步模式获取到音频数据，大小: {} 字节，任务ID: {}, 首包延迟: {} ms", 
            audioData.length, synthesizer.getLastRequestId(), synthesizer.getFirstPackageDelay());

        return TtsResponseDTO.builder()
                .status("SUCCESS")
                .taskId(synthesizer.getLastRequestId())
                .audioData(audioData)
                .format(param.getFormat().name())
                .sampleRate(param.getSampleRate())
                .firstPackageDelay(synthesizer.getFirstPackageDelay())
                .build();
    }

    // 辅助方法：将字节数组转换为十六进制字符串
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }
} 